<?php

namespace App\Models\Common;

use App\Models\BaseModel;
use App\Helpers\Code;
use Illuminate\Support\Facades\DB;

class Menu extends BaseModel
{

    protected $table = 'menus';
    protected $fillable = ['parent_id', 'name', 'path', 'icon', 'target', 'hide_menu', 'hide_breadcrumb', 'sort', 'level', 'url', 'data_scope'];
    public $params = ['name', 'path'];
    public $sorters = ['sort', 'created_at'];

    public function actions()
    {
        return $this->hasMany('App\Models\Common\MenuAction', 'menu_id');
    }

    /**
     * @param $data['level']    层级
     * @param $data['parent_id']    父级id
     * @return int|null
     */
    public function addMenu($data)
    {
        $level = (int) $data['level'];

        try {
            DB::beginTransaction();
            switch ($level) {
                case 1:
                    if (isset($data['parent_id'])) {
                        return Code::INSIDE_EXIST_DEPENDENCE;
                    }
                    break;
                case 2:
                    if (isset($data['parent_id'])) {
                        $menu = Menu::where('level', 1)->find($data['parent_id']);

                        if (!$menu) {
                            return null;
                        }

                        $menu->child_number += 1;
                        $menu->save();
                    } else {
                        return Code::INSIDE_EXIST_DEPENDENCE;
                    }
                    break;
                case 3:
                    if (isset($data['parent_id'])) {
                        $menu = Menu::where('level', 2)->find($data['parent_id']);

                        if (!$menu) {
                            return null;
                        }

                        $menu->child_number += 1;
                        $menu->save();
                    } else {
                        return Code::INSIDE_EXIST_DEPENDENCE;
                    }
                    break;
            }
            
            $data['data_scope'] = $this->getDataScope();
            $res = $this->create($data);
            DB::commit();
            return $res;
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->throwError($e);
        }
    }

    /**
     * 删除菜单
     * @param $id  菜单id
     * @return bool|int|null
     */
    public function deleteMenu($id)
    {
        $model = $this->find($id);

        if (!$model) {
            return null;
        } else {

            //判断子菜单是否存在
            $child = Menu::where('parent_id', $id)->first();
            if ($child) {
                return Code::INSIDE_EXIST_DEPENDENCE;
            }

            //判断菜单下是否存在功能
            $menuAction = MenuAction::where('menu_id', $id)->count();
            if ($menuAction > 0) {
                return Code::INSIDE_ERROR;
            }
        }
        try {
            DB::beginTransaction();
            if ($model->delete()) {

                //减去父级里面子集菜单数量
                if ($model->parent_id) {
                    $this->_minusChildNumber($model->parent_id);
                }
                DB::commit();
                return true;
            }
            DB::rollBack();
            return null;
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->throwError($e);
        }
    }

    /**
     * 菜单编辑
     * @param $id
     * @param $data
     * @return int|null
     */
    public function editMenu($id, $data)
    {
        $model = $this->find($id);

        if (!$model) {
            return null;
        }

        $level = (int) $data['level'];
        try {
            DB::beginTransaction();
            switch ($level) {
                case 1:
                    if (isset($data['parent_id'])) {
                        return Code::INSIDE_BEYOND_LIMIT;
                    }

                    switch ($model->level) {
                        case 1:
                            break;
                        case 2:
                            $this->_minusChildNumber($model->parent_id);
                            $data['parent_id'] = null;
                            $children = Menu::where('parent_id', $model->id)->where('level', 3)->get(['id'])->pluck('id')->toArray();
                            if (count($children)) {
                                foreach ($children as $item) {
                                    $child = Menu::find($item);
                                    $child->level = 2;
                                    $child->save();
                                }
                            }
                            break;
                        case 3:
                            $this->_minusChildNumber($model->parent_id);
                            $data['parent_id'] = null;
                            break;
                    }
                    break;
                case 2:
                    if (isset($data['parent_id'])) {
                        if ($model->id === (int) $data['parent_id']) {
                            return Code::INSIDE_EXIST_DEPENDENCE;
                        }

                        $menu = Menu::where('level', 1)->find($data['parent_id']);

                        if (!$menu) {
                            return Code::INSIDE_ERROR;
                        }

                        if ($model->parent_id !== (int) $data['parent_id']) {

                            $children = Menu::where('parent_id', $id)->get(['id'])->pluck('id')->toArray();

                            if (in_array((int) $data['parent_id'], $children)) {
                                return Code::INSIDE_EXIST_DEPENDENCE;
                            }

                            switch ($model->level) {
                                case 1:
                                    $children = Menu::where('parent_id', $model->id)->where('level', 2)->get(['id'])->pluck('id')->toArray();
                                    if (count($children)) {
                                        foreach ($children as $item) {
                                            $child = Menu::find($item);
                                            $child->level = 3;
                                            $child->save();
                                        }
                                    }
                                    $this->_addChildNumber($data['parent_id']);
                                    break;
                                case 2:
                                    $this->_addChildNumber($data['parent_id']);
                                    $this->_minusChildNumber($model->parent_id);
                                    break;
                                case 3:
                                    $this->_addChildNumber($data['parent_id']);
                                    $this->_minusChildNumber($model->parent_id);
                                    break;
                            }
                        }
                    } else {
                        return Code::INSIDE_BEYOND_LIMIT;
                    }
                    break;
                case 3:
                    if (isset($data['parent_id'])) {
                        if ($model->id === (int) $data['parent_id']) {
                            return Code::INSIDE_EXIST_DEPENDENCE;
                        }

                        $menu = Menu::where('level', 2)->find($data['parent_id']);

                        if (!$menu) {
                            return Code::INSIDE_ERROR_1;
                        }

                        if ($model->parent_id !== (int) $data['parent_id']) {
                            $children = Menu::where('parent_id', $id)->get(['id'])->pluck('id')->toArray();

                            if (in_array((int) $data['parent_id'], $children)) {
                                return Code::INSIDE_EXIST_DEPENDENCE;
                            }

                            switch ($model->level) {
                                case 1:
                                    $children = Menu::where('parent_id', $model->id)->where('level', 2)->get(['id'])->pluck('id')->toArray();
                                    if (count($children)) {
                                        return Code::INSIDE_ERROR;
                                    } else {
                                        $this->_addChildNumber($data['parent_id']);
                                    }
                                    break;
                                case 2:
                                    $children = Menu::where('parent_id', $model->id)->where('level', 3)->get(['id'])->pluck('id')->toArray();
                                    if (count($children)) {
                                        return Code::INSIDE_ERROR;
                                    } else {
                                        $this->_minusChildNumber($model->parent_id);
                                        $this->_addChildNumber($data['parent_id']);
                                    }
                                    break;
                                case 3:
                                    $this->_addChildNumber($data['parent_id']);
                                    $this->_minusChildNumber($model->parent_id);
                                    break;
                            }
                        }
                    } else {
                        return Code::INSIDE_BEYOND_LIMIT;
                    }
                    break;
            }

            $res = $model->updateOrCreate(['id' => $id], $data);
            DB::commit();
            return $res;
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->throwError($e);
        }
    }

    public function getMenuList($limit, $sorter, $search)
    {
        $perPage = $limit > 0 ? $limit : $this->limit;
        $model = $this->where('level', 1)->orderBy($sorter[0], $sorter[1]);
        $model->select(['id', 'parent_id', 'name', 'path', 'icon', 'target', 'hide_menu', 'hide_breadcrumb', 'child_number', 'url', 'sort']);

        foreach ($search as $key => $value) {
            switch ($key) {
                case 'name':
                case 'path':
                    $model->where($key, 'like', '%' . $value . '%');
                    break;
            }
        }

        return $model->paginate($perPage)->toArray();
    }

    public function getMenuListAll()
    {
        return $this->all(['id', 'name'])->toArray();
    }

    protected function _getMenuTree($level, $parentId = null)
    {
        $data = [];

        $fillable = ['id', 'name', 'path', 'hide_menu', 'hide_breadcrumb', 'level', 'child_number'];

        $model = $this->orderBy('sort', 'asc')->orderBy('created_at', 'asc')->where('level', $level + 1);

        if ($parentId) {
            $model->where('parent_id', $parentId);
        }

        $menus = $model->get($fillable)->toArray();

        if (count($menus)) {
            foreach ($menus as $menu) {
                $item = [];
                $item['id'] = $menu['id'];
                $item['name'] = $menu['name'];
                $item['path'] = $menu['path'];
                $item['hide_menu'] = $menu['hide_menu'];
                $item['hide_breadcrumb'] = $menu['hide_breadcrumb'];
                if ($menu['child_number'] > 0) {
                    $item['children'] = $this->_getMenuTree($level + 1, $menu['id']);
                }
                $data[] = $item;
            }
        }

        return $data;
    }

    public function getMenuTree()
    {
        return $this->_getMenuTree(0);
    }

    public function getMenuChildList($id)
    {
        $model = $this->orderBy('sort', 'asc')->orderBy('created_at', 'asc')->where('parent_id', $id)
                ->get(['id', 'parent_id', 'name', 'path', 'icon', 'target', 'hide_menu', 'hide_breadcrumb', 'sort', 'level', 'child_number']);

        if (!$model) {
            return null;
        }

        return $model->toArray();
    }

    public function getMenu($id)
    {
        $model = $this->find($id, ['id', 'parent_id', 'name', 'path', 'icon', 'target', 'hide_menu', 'hide_breadcrumb', 'sort', 'level', 'child_number', 'url']);

        if (!$model) {
            return null;
        }

        return $model->toArray();
    }

    public function getMenuActionAll($id)
    {
        $model = $this->find($id);

        if (!$model) {
            return null;
        }

        $data = $model->with('actions:id,menu_id,title,code,description,created_at,updated_at')->where('id', $id)->first(['id'])->toArray();
        if (isset($data['actions']) && count($data['actions'])  > 0) {
            return $data['actions'];
        } else {
            return [];
        }
    }

    protected function _addChildNumber($id)
    {
        $parent = Menu::find($id);
        $parent->child_number += 1;
        $parent->save();
    }

    protected function _minusChildNumber($id)
    {
        $parent = Menu::find($id);
        $nums = $parent->child_number - 1;
        $parent->child_number = $nums >= 0 ? $nums : 0;
        $parent->save();
    }

}
